﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Threading;

namespace Flashloader_Core
{
    static class Message
    {
        static Semaphore WaitSerialReplaySema = new Semaphore(0, 1);
        static Dictionary<string, byte[]> CmdList = new Dictionary<string, byte[]>
        {
            { "CmdCheckBootStatus",new byte[]{0xAA, 0x55, 0x0A, 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0xCE , 0x5F} },
            { "CmdCheckRandomCode",new byte[]{0xAA, 0x55, 0x0A, 0x00 , 0x02 , 0x00 , 0x00 , 0x00 , 0xCE , 0x1B} },
            { "CmdEraseFlash",new byte[]{0xAA, 0x55, 0x0D, 0x00 , 0x03 , 0x00 , 0x00 , 0x19 , 0x29, 0x39, 0x49 ,0x58, 0xCD} },
        };
        static public SerialPort Port { get; set; } = new SerialPort();
        static public byte[] ReceiveData { get; set; } = new byte[10000];
        static public int ReceiveLength { get; set; } = 0;
        static public bool OpenSerialPort(string COMx)
        {
            bool bRet;
            Port.PortName = COMx;
            Port.BaudRate = 115200;
            Port.DataBits = 8;
            Port.Parity = Parity.None;
            Port.StopBits = StopBits.One;
            Port.DataReceived += Port_DataReceived;
            try
            {
                Port.Open();
                bRet = Port.IsOpen;
            }
            catch (Exception ex)
            {
                Console.WriteLine("串口打开错误：" + ex.Message);
                bRet = false;
            }
            return bRet;
        }

        private static void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int BuffLength = Port.BytesToRead;

            Port.Read(ReceiveData, ReceiveLength, BuffLength);
            ReceiveLength += BuffLength;
            if (ReceiveLength >= 8)
            {
                var length = ReceiveData[3] << 8 | ReceiveData[2];
                if (ReceiveLength >= length)
                {
                    WaitSerialReplaySema.Release();
                }
            }

        }

        public enum BootStatus
        {
            ERROR_STATUS,
            ON_BOOT_STATUS,
            ON_APP_STATUS,
        }
        static public BootStatus CheckBootStates()
        {
            BootStatus BsRet = BootStatus.ON_BOOT_STATUS;
            ReceiveLength = 0;
            Array.Clear(ReceiveData, 0, ReceiveLength);
            Port.Write(CmdList["CmdCheckBootStatus"], 0, CmdList["CmdCheckBootStatus"].Length);
            WaitSerialReplaySema.WaitOne(10000);
            byte[] Crc = new byte[2];
            Crc = ToModbus(ReceiveData, ReceiveLength);
            if (Crc[0] == 0x00 && Crc[1] == 0x00)
            {
                switch (ReceiveData[8])
                {
                    case 0x00:
                        BsRet = BootStatus.ON_BOOT_STATUS;
                        break;
                    case 0x01:
                        BsRet = BootStatus.ON_APP_STATUS;
                        break;
                }
            }
            else
            {
                Console.WriteLine("\t---通讯错误");
            }
            return BsRet;
        }

        static public byte[] CheckRandomCode()
        {
            byte[] Code = new byte[4];
            ReceiveLength = 0;
            Array.Clear(ReceiveData, 0, ReceiveLength);
            Port.Write(CmdList["CmdCheckRandomCode"], 0, CmdList["CmdCheckRandomCode"].Length);
            WaitSerialReplaySema.WaitOne(10000);
            byte[] Crc = new byte[2];
            Crc = ToModbus(ReceiveData, ReceiveLength);
            if (Crc[0] == 0x00 && Crc[1] == 0x00)
            {
                Code[0] = ReceiveData[8];
                Code[1] = ReceiveData[9];
                Code[2] = ReceiveData[10];
                Code[3] = ReceiveData[11];
            }
            else
            {
                Console.WriteLine("\t---通讯错误");
            }
            return Code;
        }

        static public bool EraseFlash(byte[] PinCode)
        {
            bool bRet = false;
            byte[] Code = new byte[4];
            ReceiveLength = 0;
            Array.Clear(ReceiveData, 0, ReceiveLength);
            CmdList["CmdEraseFlash"][10] = PinCode[3];
            CmdList["CmdEraseFlash"][9] = PinCode[2];
            CmdList["CmdEraseFlash"][8] = PinCode[1];
            CmdList["CmdEraseFlash"][7] = PinCode[0];
            byte[] Crc = new byte[2];
            Crc = ToModbus(CmdList["CmdEraseFlash"], CmdList["CmdEraseFlash"].Length - 2);
            CmdList["CmdEraseFlash"][11] = Crc[0];
            CmdList["CmdEraseFlash"][12] = Crc[1];
            Port.Write(CmdList["CmdEraseFlash"], 0, CmdList["CmdEraseFlash"].Length);
            int i;
            for (i = 0; i < 30; i++)
            {
                Console.WriteLine("\t\t等待Flash擦除" + i + "...");
                Crc = ToModbus(ReceiveData, ReceiveLength);
                if (Crc[0] == 0x00 && Crc[1] == 0x00)
                {
                    if (ReceiveData[7] == 0x01)
                    {
                        bRet = true;
                        break;
                    }
                }
                Thread.Sleep(1000);
            }
            if(i == 30)
            {
                Console.WriteLine("\t---等待Flash擦除超时");
            }
            

            return bRet;
        }

        public static byte[] ToModbus(byte[] byteData, int length)
        {
            byte[] CRC = new byte[2];

            UInt16 wCrc = 0xFFFF;
            for (int i = 0; i < length; i++)
            {
                wCrc ^= Convert.ToUInt16(byteData[i]);
                for (int j = 0; j < 8; j++)
                {
                    if ((wCrc & 0x0001) == 1)
                    {
                        wCrc >>= 1;
                        wCrc ^= 0xA001;
                    }
                    else
                    {
                        wCrc >>= 1;
                    }
                }
            }

            CRC[1] = (byte)((wCrc & 0xFF00) >> 8);
            CRC[0] = (byte)(wCrc & 0x00FF);
            return CRC;

        }
    }
}
